/*******************************************************************************
 * Copyright 2007, 2009 InnovaSoft SA http://www.innovasoft.biz/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

package biz.innovasoft.nodb.prevayler;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;

import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;

import org.prevayler.Prevayler;
import org.prevayler.PrevaylerFactory;

import biz.innovasoft.nodb.RepositoryException;
import biz.innovasoft.nodb.annotations.Query;
import biz.innovasoft.nodb.annotations.Transaction;
import biz.innovasoft.nodb.annotations.TransactionWithQuery;
import biz.innovasoft.nodb.transaction.AnnotatedQuery;
import biz.innovasoft.nodb.transaction.AnnotatedTransaction;
import biz.innovasoft.nodb.transaction.AnnotatedTransactionWithQuery;

public class PrevalentProxy implements InvocationHandler {

	protected Prevayler prevayler;
	protected Object prevSystem;
	protected Object proxy;
	
	protected PrevalentProxy() {}
	
	public PrevalentProxy(Class<?> []iface, Object system)
			throws RepositoryException {
		this(iface, system, "./prevalence/"
				+ system.getClass().getCanonicalName());
	}

	public PrevalentProxy(Class<?> []iface, Object system, String path)
			throws RepositoryException {
		this.proxy = newInstance(iface);

		try {
			this.prevayler = PrevaylerFactory.createPrevayler(
					(Serializable) system, path);
			this.prevSystem = prevayler.prevalentSystem();

		} catch (Exception e) {
			throw new RuntimeException("Can't open prevayler system.", e);
		}
	}

	public PrevalentProxy (PrevaylerFactory factory, Class<?> []iface, Object system, String path ) {
		this.proxy = newInstance(iface);
		try {
			factory.configurePrevalentSystem(system);
			factory.configurePrevalenceDirectory(path);
			this.prevayler = factory.create();
			this.prevSystem = prevayler.prevalentSystem();
		} catch (Exception e) {
			throw new RuntimeException("Can't open prevalent system at location: " + path , e);
		}
	}
	
	public Object getPrevaylerSystem() {
		return proxy;
	}

	protected Object newInstance(Class<?> []cls) {
		Object proxy = Proxy.newProxyInstance(cls[0].getClassLoader(),
				cls, this);
		return proxy;
	}

	protected void close () throws RepositoryException {
		try {
			this.prevayler.close();
		} catch (IOException e) {
			throw new RepositoryException ("Error closing the repository");
		} 
	}
	
	public void takeSnapshot() {
		try {
			prevayler.takeSnapshot();
		} catch (Exception e) {
			throw new RuntimeException(
					"Error taking snapshot. PrevalentProxy base class:"
							+ (prevSystem), e);
		}
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if (method.isAnnotationPresent(TransactionWithQuery.class)) {
			Class<?> types[] = method.getParameterTypes();
			return prevayler.execute(new AnnotatedTransactionWithQuery(method
					.getName(), types, args));
		} else if (method.isAnnotationPresent(Transaction.class)) {
			Class<?> types[] = method.getParameterTypes();
			prevayler.execute(new AnnotatedTransaction(method.getName(), types,
					args));
			return null;
		} else if (method.isAnnotationPresent(Query.class)) {
			Class<?> types[] = method.getParameterTypes();
			return prevayler.execute(new AnnotatedQuery(method.getName(),
					types, args));
		}
		if (method.getName().equals("takeSnapshot")) {
			takeSnapshot();
			return null;
		}
		return method.invoke(this.prevSystem, args);
	}
	
	

}
